// filesys.cc 
//	Routines to manage the overall operation of the file system.
//	Implements routines to map from textual file names to files.
//
//	Each file in the file system has:
//	   A file header, stored in a sector on disk 
//		(the size of the file header data structure is arranged
//		to be precisely the size of 1 disk sector)
//	   A number of data blocks
//	   An entry in the file system directory
//
// 	The file system consists of several data structures:
//	   A bitmap of free disk sectors (cf. bitmap.h)
//	   A directory of file names and file headers
//
//      Both the bitmap and the directory are represented as normal
//	files.  Their file headers are located in specific sectors
//	(sector 0 and sector 1), so that the file system can find them 
//	on bootup.
//
//	The file system assumes that the bitmap and directory files are
//	kept "open" continuously while Nachos is running.
//
//	For those operations (such as Create, Remove) that modify the
//	directory and/or bitmap, if the operation succeeds, the changes
//	are written immediately back to disk (the two files are kept
//	open during all this time).  If the operation fails, and we have
//	modified part of the directory and/or bitmap, we simply discard
//	the changed version, without writing it back to disk.
//
// 	Our implementation at this point has the following restrictions:
//
//	   there is no synchronization for concurrent accesses
//	   files have a fixed size, set when the file is created
//	   files cannot be bigger than about 3KB in size
//	   there is no hierarchical directory structure, and only a limited
//	     number of files can be added to the system
//	   there is no attempt to make the system robust to failures
//	    (if Nachos exits in the middle of an operation that modifies
//	    the file system, it may corrupt the disk)
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.
#ifndef FILESYS_STUB

#include "copyright.h"
#include "debug.h"
#include "disk.h"
#include "pbitmap.h"
#include "directory.h"
#include "filehdr.h"
#include "filesys.h"
#include "list.h"
#include <string.h>
// Sectors containing the file headers for the bitmap of free sectors,
// and the directory of files.  These file headers are placed in well-known 
// sectors, so that they can be located on boot-up.
#define FreeMapSector 		0
#define RootDirectorySector 	1
#define FileNameMaxLen 		9
// Initial file sizes for the bitmap and directory; until the file system
// supports extensible files, the directory size sets the maximum number 
// of files that can be loaded onto the disk.
#define FreeMapFileSize 	(NumSectors / BitsInByte)
#define NumDirEntries 		2
//#define NumSubDirectory     10
#define DirectoryFileSize 	(sizeof(DirectoryEntry) * NumDirEntries)

static const int TransferSize = 128;

//----------------------------------------------------------------------
// FileSystem::FileSystem
// 	Initialize the file system.  If format = TRUE, the disk has
//	nothing on it, and we need to initialize the disk to contain
//	an empty directory, and a bitmap of free sectors (with almost but
//	not all of the sectors marked as free).  
//
//	If format = FALSE, we just have to open the files
//	representing the bitmap and the directory.
//
//	"format" -- should we initialize the disk?
//----------------------------------------------------------------------

FileSystem::FileSystem(bool format)
{ 
    DEBUG(dbgFile, "Initializing the file system.");
    if (format) {
        PersistentBitmap *freeMap = new PersistentBitmap(NumSectors);
        Directory *directory = new Directory(NumDirEntries);
	FileHeader *mapHdr = new FileHeader;
	FileHeader *dirHdr = new FileHeader;
    DEBUG(dbgFile, "Formatting the file system.");
    // First, allocate space for FileHeaders for the directory and bitmap
    // (make sure no one else grabs these!)
	freeMap->Mark(FreeMapSector);	    
	freeMap->Mark(RootDirectorySector);

    // Second, allocate space for the data blocks containing the contents
    // of the directory and bitmap files.  There better be enough space!

	ASSERT(mapHdr->Allocate(freeMap, FreeMapFileSize));
	ASSERT(dirHdr->Allocate(freeMap, DirectoryFileSize));

    // Flush the bitmap and directory FileHeaders back to disk
    // We need to do this before we can "Open" the file, since open
    // reads the file header off of disk (and currently the disk has garbage
    // on it!).

        DEBUG(dbgFile, "Writing headers back to disk.");
	mapHdr->WriteBack(FreeMapSector);    
	dirHdr->WriteBack(RootDirectorySector);
    // OK to open the bitmap and directory files now
    // The file system operations assume these two files are left open
    // while Nachos is running.

        freeMapFile = new OpenFile(FreeMapSector);
        directoryFile = new OpenFile(RootDirectorySector);
     
    // Once we have the files "open", we can write the initial version
    // of each file back to disk.  The directory at this point is completely
    // empty; but the bitmap has been changed to reflect the fact that
    // sectors on the disk have been allocated for the file headers and
    // to hold the file data for the directory and bitmap.

        DEBUG(dbgFile, "Writing bitmap and directory back to disk.");
	freeMap->WriteBack(freeMapFile);	 // flush changes to disk
	directory->SetFatherDir(RootDirectorySector);	
	directory->WriteBack(directoryFile);

	if (debug->IsEnabled('f')) {
	    freeMap->Print();
	    directory->Print();
        }
        delete freeMap; 
	delete directory; 
	delete mapHdr; 
	delete dirHdr;
    } else {
    // if we are not formatting the disk, just open the files representing
    // the bitmap and directory; these are left open while Nachos is running
        freeMapFile = new OpenFile(FreeMapSector);
        directoryFile = new OpenFile(RootDirectorySector);
    }
	currentDirHdrSector = RootDirectorySector;
	workingDir.push_back("/");
}

//----------------------------------------------------------------------
// FileSystem::Create
// 	Create a file in the Nachos file system (similar to UNIX create).
//	Since we can't increase the size of files dynamically, we have
//	to give Create the initial size of the file.
//
//	The steps to create a file are:
//	  Make sure the file doesn't already exist
//        Allocate a sector for the file header
// 	  Allocate space on disk for the data blocks for the file
//	  Add the name to the directory
//	  Store the new file header on disk 
//	  Flush the changes to the bitmap and the directory back to disk
//
//	Return TRUE if everything goes ok, otherwise, return FALSE.
//
// 	Create fails if:
//   		file is already in directory
//	 	no free space for file header
//	 	no free entry for file in directory
//	 	no free space for data blocks for the file 
//
// 	Note that this implementation assumes there is no concurrent access
//	to the file system!
//
//	"name" -- name of file to be created
//	"initialSize" -- size of file to be created
//----------------------------------------------------------------------
/*
bool
FileSystem::CreateFile(char *name, int initialSize)
{
    Directory *directory;
    PersistentBitmap *freeMap;
    FileHeader *hdr;
    int sector;
    bool success = TRUE;

    DEBUG(dbgFile, "Creating file " << name << " size " << initialSize);

//	currentDirHdrSector = 1;
	directoryFile = new OpenFile(currentDirHdrSector);
    directory = new Directory();
    directory->FetchFrom(directoryFile);
    if (directory->Find(name) != -1)
	{
		printf("file is already in directory!\n");
		success = FALSE;			// file is already in directory
	}
    else 
	{	
        freeMap = new PersistentBitmap(freeMapFile,NumSectors);
        sector = freeMap->FindAndSet();	// find a sector to hold the file header
    	if (sector == -1) 		
		{
				cout<< "in create file add  success = "<<success<<endl;
			printf("no free block for file header!\n");
            success = FALSE;		// no free block for file header 
s		}
        else if (!directory->Add(name, sector, true))
		{
			cout<< "in create file add"<<endl;
			hdr = new FileHeader;
		    hdr->FetchFrom(currentDirHdrSector);
			ASSERT(hdr->Allocate(freeMap, DirectoryFileSize));
   	    	hdr->WriteBack(currentDirHdrSector);
			directory->AddListNode(NumDirEntries);
			directory->WriteBack(directoryFile);
			freeMap->Clear(sector);
	    	freeMap->WriteBack(freeMapFile);
			CreateFile(name, initialSize);
		}
		else 
		{
    	    hdr = new FileHeader;
		    if (!hdr->Allocate(freeMap, initialSize))
			{
				printf("no space on disk for data!\n");
            	success = FALSE;	// no space on disk for data
			}
		    else 
			{	
		    	success = TRUE;
				// everthing worked, flush all changes back to disk
    	    	hdr->WriteBack(sector); 		
    	    	directory->WriteBack(directoryFile);
    	    	freeMap->WriteBack(freeMapFile);
	   		}
            delete hdr;
	}
        delete freeMap;
    }
    delete directory;
    return success;
}
*/
bool
FileSystem::CreateFile(char *name, int initialSize)
{
    Directory *directory;
    PersistentBitmap *freeMap;
    FileHeader *hdr;
    int sector;
    bool success = TRUE;

    DEBUG(dbgFile, "Creating file " << name << " size " << initialSize);
	if(!strncmp(name, "./", 2))
	{
		ChangeDirectory(name);
		char *t, *pre = name;
		while(t = strchr(pre,'/'))
		{
			pre = t + 1;
		}
		strncpy(name, pre, FileNameMaxLen);
	}
	directoryFile = new OpenFile(currentDirHdrSector);
    directory = new Directory();
    directory->FetchFrom(directoryFile);
    if (directory->Find(name) != -1)
	{
		printf("file is already in directory!\n");
		success = FALSE;			// file is already in directory
	}
    else 
	{	
        freeMap = new PersistentBitmap(freeMapFile,NumSectors);
        sector = freeMap->FindAndSet();	// find a sector to hold the file header
    	if (sector == -1) 		
		{
				cout<< "in create file add  success = "<<success<<endl;
			printf("no free block for file header!\n");
            success = FALSE;		// no free block for file header 
		}
        else if (!directory->Add(name, sector, true))
		{
			cout<< "in create file add"<<endl;
			hdr = new FileHeader;
		    hdr->FetchFrom(currentDirHdrSector);
			ASSERT(hdr->Allocate(freeMap, DirectoryFileSize));
   	    	hdr->WriteBack(currentDirHdrSector);
			directory->AddListNode(NumDirEntries);
			directory->WriteBack(directoryFile);
			freeMap->Clear(sector);
	    	freeMap->WriteBack(freeMapFile);
			CreateFile(name, initialSize);
		}
		else 
		{
    	    hdr = new FileHeader;
		    if (!hdr->Allocate(freeMap, initialSize))
			{
				printf("no space on disk for data!\n");
            	success = FALSE;	// no space on disk for data
			}
		    else 
			{	
		    	success = TRUE;
				// everthing worked, flush all changes back to disk
    	    	hdr->WriteBack(sector); 		
    	    	directory->WriteBack(directoryFile);
    	    	freeMap->WriteBack(freeMapFile);
	   		}
            delete hdr;
	}
        delete freeMap;
    }
    delete directory;
    return success;
}


bool 
FileSystem::CreateDirectory(char *name)
{
	Directory *directory;
	Directory *subDirectory;
	OpenFile* subDirectoryFile;
    PersistentBitmap *freeMap;
    FileHeader *hdr;
    int sector;
    bool success = TRUE;

	DEBUG(dbgFile, "Creating folder " << name);
	//currentDirHdrSector = 1;
	directory = new Directory();
	directoryFile = new OpenFile(currentDirHdrSector);
    directory->FetchFrom(directoryFile);
	//directory->List();
	if (directory->Find(name) != -1)
	{
		printf("folder is already in directory!\n");
      	success = FALSE;			// folder is already in directory
	}
	else 
	{
        freeMap = new PersistentBitmap(freeMapFile,NumSectors);
        sector = freeMap->FindAndSet();	// find a sector to hold the directory file header
    	if (sector == -1)
		{
			printf("no free block for file header!\n");
            success = FALSE;		// no free block for file header 
		}
        else if (!directory->Add(name, sector, false))
		{
			hdr = new FileHeader;
		    hdr->FetchFrom(currentDirHdrSector);
			ASSERT(hdr->Allocate(freeMap, DirectoryFileSize));
   	    	hdr->WriteBack(currentDirHdrSector);
			directory->AddListNode(NumDirEntries);
			directory->WriteBack(directoryFile);
			freeMap->Clear(sector);
	    	freeMap->WriteBack(freeMapFile);
			CreateDirectory(name);
		}
		else 
		{
    	    hdr = new FileHeader;
	 	    if (!hdr->Allocate(freeMap, DirectoryFileSize))
			{
				printf("no space on disk for data\n");
            	success = FALSE;	// no space on disk for data
			}
	    	else 
			{	
		    	success = TRUE;
				// everthing worked, flush all changes back to disk
    	    	hdr->WriteBack(sector); 
				subDirectory = new Directory(NumDirEntries);
			 	subDirectoryFile = new OpenFile(sector);	
				subDirectory->SetFatherDir(currentDirHdrSector);	
				subDirectory->WriteBack(subDirectoryFile);
    	    	directory->WriteBack(directoryFile);
    	    	freeMap->WriteBack(freeMapFile);
 		    }
            delete hdr;
		}
        delete freeMap;
    }
	delete subDirectory;
    delete directory;
	delete subDirectoryFile;
    return success;	
	
}

bool
FileSystem::ChangeDirectory(char *name)
{
	
	Directory *directory;
	bool success = true;
    int tmpSector = -1;
	char** subName = new char*[4];

	for(int i = 0; i < 4; i ++)
		subName[i] = new char[FileNameMaxLen + 1];

	DEBUG(dbgFile, "Changing Directory to " << name);
	directory = new Directory();
	directoryFile = new OpenFile(currentDirHdrSector);
    directory->FetchFrom(directoryFile);
	if(!strncmp(name, "..", FileNameMaxLen))	//change directory to father directory
	{
		if(currentDirHdrSector == RootDirectorySector)
		{
			//printf("current directory is root directory!\n");
		    delete directory;
			return TRUE;
		}
		else
		{
			tmpSector = directory->GetFatherDir();
			if (tmpSector == -1)
			{
				printf("No such Directory!\n");
				success = false;
			}
			else
			{
				currentDirHdrSector = tmpSector;
				directoryFile = new OpenFile(currentDirHdrSector);
				workingDir.pop_back();
			}
		}
	}
	else if(!strncmp(name, ".", FileNameMaxLen))
		return TRUE;
	else if(!strncmp(name, "./", 2))
	{
		char *t, *pre = name;
		int l;
		int i = 0;
		while(t = strchr(pre,'/'))
		{
			l = t - pre;
			strncpy(subName[i], pre, l);
			subName[i][l] = '\0';
			i ++;
			pre = t + 1;
		}
		for(int j = 0; j < i; j ++)
		{
			ChangeDirectory(subName[j]);
		}
	}
	else
	{
		tmpSector = directory->Find(name);
		if (tmpSector == -1)
		{
			printf("No such Directory!\n");
			success = false;
		}
		else
		{
			currentDirHdrSector = tmpSector;
			directoryFile = new OpenFile(currentDirHdrSector);
			workingDir.push_back(name);
		}
	}
    delete directory;
	return success;
}
//----------------------------------------------------------------------
// FileSystem::Open
// 	Open a file for reading and writing.  
//	To open a file:
//	  Find the location of the file's header, using the directory 
//	  Bring the header into memory
//
//	"name" -- the text name of the file to be opened
//----------------------------------------------------------------------
/*
OpenFile *
FileSystem::Open(char *name)
{ 
    Directory *directory = new Directory();
    OpenFile *openFile = NULL;
    int sector;

    DEBUG(dbgFile, "Opening file" << name);
    directory->FetchFrom(directoryFile);
    sector = directory->Find(name); 
    if (sector >= 0) 		
		openFile = new OpenFile(sector);	// name was found in directory 
    delete directory;
    return openFile;				// return NULL if not found
}
*/
OpenFile *
FileSystem::Open(char *name)
{ 
    Directory *directory = new Directory();
    OpenFile *openFile = NULL;
    int sector;
    
    DEBUG(dbgFile, "Opening file" << name);
	if(!strncmp(name, "./", 2))
	{
		ChangeDirectory(name);
		char *t, *pre = name;
		while(t = strchr(pre,'/'))
		{
			pre = t + 1;
		}
		strncpy(name, pre, FileNameMaxLen);
	}
	directoryFile = new OpenFile(currentDirHdrSector);
    directory->FetchFrom(directoryFile);
	
	sector = directory->Find(name); 
    if (sector >= 0) 		
		openFile = new OpenFile(sector);	// name was found in directory 
    delete directory;
    return openFile;				// return NULL if not found
}

//----------------------------------------------------------------------
// FileSystem::Remove
// 	Delete a file from the file system.  This requires:
//	    Remove it from the directory
//	    Delete the space for its header
//	    Delete the space for its data blocks
//	    Write changes to directory, bitmap back to disk
//
//	Return TRUE if the file was deleted, FALSE if the file wasn't
//	in the file system.
//
//	"name" -- the text name of the file to be removed
//----------------------------------------------------------------------

bool
FileSystem::Remove(char *name)
{ 
    Directory *directory;
    int sector;
    char** names;
	int num;

    directory = new Directory();
    directory->FetchFrom(directoryFile);//get the file or folder information
    sector = directory->Find(name);
    if (sector == -1) 
	{
       delete directory;
       return FALSE;			 // file not found 
    }
	if(directory->IsLeaf(name))
	{
		ASSERT(RemoveFile(name));
	}
	else
	{
		ChangeDirectory(name);
		directory->FetchFrom(directoryFile);
		names = directory->GetNames();
		num = directory->GetNumInUse();
		for(int i = 0; i < num; i ++)
		{
			Remove(names[i]);
		}
		ChangeDirectory("..");
		RemoveFile(name);
	}
	
	delete directory;
	delete names;
	return TRUE;
} 
bool
FileSystem::RemoveFile(char *name)
{
	Directory *directory;
    PersistentBitmap *freeMap;
    FileHeader *fileHdr;
    int sector;
    
    directory = new Directory();
    directory->FetchFrom(directoryFile);
    sector = directory->Find(name);
    if (sector == -1) 
	{
       delete directory;
       return FALSE;			 // file not found 
    }
    fileHdr = new FileHeader;
    fileHdr->FetchFrom(sector);

    freeMap = new PersistentBitmap(freeMapFile,NumSectors);

    fileHdr->Deallocate(freeMap);  		// remove data blocks
    freeMap->Clear(sector);			// remove header block
    directory->Remove(name);

    freeMap->WriteBack(freeMapFile);		// flush to disk
    directory->WriteBack(directoryFile);        // flush to disk
    delete fileHdr;
    delete directory;
    delete freeMap;
    return TRUE;
} 

void
FileSystem::Copy(char *from, char *to)
{
    OpenFile* srcFile;
    OpenFile* desFile;   
    int amountRead, fileLength;
    char *buffer;
    
// Open NACHOS file
    if ((srcFile = kernel->fileSystem->Open(from)) == NULL) {       
        printf("Append: couldn't open input file %s\n", from);
        return;
    } 
    
// Figure out length of NACHOS file
    fileLength = srcFile->Length();

// Create a Nachos file of the same length
    DEBUG('f', "Copying file " << from << " of size " << fileLength <<  " to file " << to);
    if (!kernel->fileSystem->CreateFile(to, fileLength)) {   // Create Nachos file
        printf("Copy: couldn't create output file %s\n", to);
        return;
    }
    
    desFile = kernel->fileSystem->Open(to);
    ASSERT(desFile != NULL);
    
// Copy the data in TransferSize chunks
    buffer = new char[TransferSize];
    while ((amountRead=srcFile->Read(buffer, TransferSize)) > 0)
        desFile->Write(buffer, amountRead);    
    delete [] buffer;

// Close the Nachos files
    delete srcFile;
	delete desFile;
}


void
FileSystem::Append(char *from, char *to)
{
    OpenFile* srcFile;
    OpenFile* desFile;
    int amountRead, fileLength, appendStart, appendEnd;
    char *buffer;
    
// Open NACHOS file
    if ((srcFile = kernel->fileSystem->Open(from)) == NULL) {       
        printf("Append: couldn't open input file %s\n", from);
        return;
    } 
    
// Figure out length of NACHOS file
    fileLength = srcFile->Length();
    
    if ((desFile = kernel->fileSystem->Open(to)) == NULL) {
        printf("Append: unable to open append file %s\n", to);
        return;
    }
   appendStart = desFile->Length();
   cout << "appendStart is " << appendStart << endl;
    
    if(!(desFile->AppendFileSize(fileLength))){
         printf("The appending file is too big!\nAppending file failed\n");
         return;
    }
    
   //write appended content back to the disk
   buffer = new char[TransferSize];
   desFile->Seek(appendStart);
   while ((amountRead=srcFile->Read(buffer, TransferSize)) > 0)
    
        //openFile->Write(buffer, amountRead);    
        desFile->WriteAt(buffer, amountRead, appendStart);

   //appendStart += amountRead;
   appendEnd = desFile->Length();
   cout << "appendEnd is " << appendEnd << endl;
// Write the inode back to the disk, because we have changed it

    desFile->WriteBack(to);
    printf("inodes have been written back\n");
}


//----------------------------------------------------------------------
// FileSystem::List
// 	List all the files in the file system directory.
//----------------------------------------------------------------------

void
FileSystem::List()
{
    Directory *directory = new Directory();
    directory->FetchFrom(directoryFile);
    directory->List();
    delete directory;
}
void 
FileSystem::PrintWorkingDir()
{
	for(list<string>::iterator it = workingDir.begin(); it != workingDir.end(); it++)
	{
		cout<<(*it);
		if(it != workingDir.begin())
			cout<<"/";
	}
	cout<<endl;
}
//----------------------------------------------------------------------
// FileSystem::Print
// 	Print everything about the file system:
//	  the contents of the bitmap
//	  the contents of the directory
//	  for each file in the directory,
//	      the contents of the file header
//	      the data in the file
//----------------------------------------------------------------------

void
FileSystem::Print()
{
    FileHeader *bitHdr = new FileHeader;
    FileHeader *dirHdr = new FileHeader;
    PersistentBitmap *freeMap = new PersistentBitmap(freeMapFile,NumSectors);
    Directory *directory = new Directory(NumDirEntries);

    printf("Bit map file header:\n");
    bitHdr->FetchFrom(FreeMapSector);
    bitHdr->Print();

    printf("Directory file header:\n");
    dirHdr->FetchFrom(RootDirectorySector);
    dirHdr->Print();

    freeMap->Print();

    directory->FetchFrom(directoryFile);
    directory->Print();

    delete bitHdr;
    delete dirHdr;
    delete freeMap;
    delete directory;
} 
void 
FileSystem::FileReName(char *oldName, char *newName)
{
	Directory *directory = new Directory();

    directory->FetchFrom(directoryFile);
	if(!directory->ReName(oldName,newName))
	{
		printf("No such file or folder %s\n", oldName);
		delete directory;
		return;
	}
	directory->WriteBack(directoryFile);        // flush to disk

	delete directory;
}
#endif // FILESYS_STUB
